package org.an.proj.contacts.util;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.IBinder;
import android.os.RemoteException;
import android.provider.Settings.Secure;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.TreeMap;

public class OpenUDIDManager implements ServiceConnection {
  public final static String PREF_KEY = "openudid";
  public final static String PREFS_NAME = "openudid_prefs";
  // public final static String TAG = "OpenUDID";

  private final static boolean LOG = true; // Display or not debug message

  private final Context mContext; // Application context
  private List<ResolveInfo> mMatchingIntents; // List of available OpenUDID
  // Intents
  private Map<String, Integer> mReceivedOpenUDIDs; // Map of OpenUDIDs found
  // so far

  private final SharedPreferences mPreferences; // Preferences to store the
  // OpenUDID
  private final Random mRandom;

  private OpenUDIDManager(Context context) {
    mPreferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
    mContext = context;
    mRandom = new Random();
    mReceivedOpenUDIDs = new HashMap<String, Integer>();
  }

  @Override public void onServiceConnected(ComponentName className, IBinder service) {
    // Get the OpenUDID from the remote service
    try {
      // Send a random number to the service
      android.os.Parcel data = android.os.Parcel.obtain();
      data.writeInt(mRandom.nextInt());
      android.os.Parcel reply = android.os.Parcel.obtain();
      service.transact(1, android.os.Parcel.obtain(), reply, 0);
      if (data.readInt() == reply.readInt()) // Check if the service
      // returns us this number
      {
        final String _openUDID = reply.readString();
        if (_openUDID != null) { // if valid OpenUDID, save it
          if (LOG)
          // com.homelink.util.LogUtil.d(TAG, "Received " + _openUDID);

          {
            if (mReceivedOpenUDIDs.containsKey(_openUDID)) {
              mReceivedOpenUDIDs.put(_openUDID, mReceivedOpenUDIDs.get(_openUDID) + 1);
            } else {
              mReceivedOpenUDIDs.put(_openUDID, 1);
            }
          }
        }
      }
    } catch (RemoteException e) {
      // if (LOG)
      // com.homelink.util.LogUtil.e(TAG, "RemoteException: " + e.getMessage());
    }
    mContext.unbindService(this);

    startService(); // Try the next one
  }

  @Override public void onServiceDisconnected(ComponentName className) {
  }

  private void storeOpenUDID() {
    final Editor e = mPreferences.edit();
    e.putString(PREF_KEY, OpenUDID);
    e.apply();
  }

  /*
   * Generate a new OpenUDID
   */
  private void generateOpenUDID() {
    // if (LOG)
    // com.homelink.util.LogUtil.d(TAG, "Generating openUDID");
    // Try to get the ANDROID_ID
    OpenUDID = Secure.getString(mContext.getContentResolver(), Secure.ANDROID_ID);
    if (OpenUDID == null || OpenUDID.equals("9774d56d682e549c") || OpenUDID.length() < 15) {
      // if ANDROID_ID is null, or it's equals to the GalaxyTab generic
      // ANDROID_ID or bad, generates a new one
      final SecureRandom random = new SecureRandom();
      OpenUDID = new BigInteger(64, random).toString(16);
    }
  }

  /*
   * Start the oldest service
   */
  private void startService() {
    if (mMatchingIntents.size() > 0) { // There are some Intents untested
      // if (LOG)
      // com.homelink.util.LogUtil.d(TAG,
      // "Trying service "
      // + mMatchingIntents.get(0).loadLabel(
      // mContext.getPackageManager()));

      final ServiceInfo servInfo = mMatchingIntents.get(0).serviceInfo;
      final Intent i = new Intent();
      i.setComponent(new ComponentName(servInfo.applicationInfo.packageName, servInfo.name));
      mContext.bindService(i, this, Context.BIND_AUTO_CREATE);
      mMatchingIntents.remove(0);
    } else { // No more service to test

      getMostFrequentOpenUDID(); // Choose the most frequent

      if (OpenUDID == null) // No OpenUDID was chosen, generate one
      {
        generateOpenUDID();
      }
      // if (LOG)
      // com.homelink.util.LogUtil.d(TAG, "OpenUDID: " + OpenUDID);

      storeOpenUDID();// Store it locally
      mInitialized = true;
    }
  }

  private void getMostFrequentOpenUDID() {
    if (!mReceivedOpenUDIDs.isEmpty()) {
      final TreeMap<String, Integer> sorted_OpenUDIDS =
          new TreeMap<String, Integer>(new ValueComparator());
      sorted_OpenUDIDS.putAll(mReceivedOpenUDIDs);

      OpenUDID = sorted_OpenUDIDS.firstKey();
    }
  }

  private static String OpenUDID = null;
  private static boolean mInitialized = false;

  /**
   * The Method to call to get OpenUDID
   *
   * @return the OpenUDID
   */
  public static String getOpenUDID() {
    // if (!mInitialized)
    // com.homelink.util.LogUtil.e("OpenUDID", "Initialisation isn't done");
    return OpenUDID;
  }

  /**
   * The Method to call to get OpenUDID
   *
   * @return the OpenUDID
   */
  public static boolean isInitialized() {
    return mInitialized;
  }

  /**
   * The Method the call at the init of your app
   *
   * @param context you current context
   */
  public static void sync(Context context) {
    // Initialise the Manager
    OpenUDIDManager manager = new OpenUDIDManager(context);

    // Try to get the openudid from local preferences
    OpenUDID = manager.mPreferences.getString(PREF_KEY, null);
    if (OpenUDID == null) // Not found
    {
      // Get the list of all OpenUDID services available (including
      // itself)
      manager.mMatchingIntents =
          context.getPackageManager().queryIntentServices(new Intent("org.OpenUDID.GETUDID"), 0);
      // if (LOG)
      // com.homelink.util.LogUtil.d(TAG, manager.mMatchingIntents.size()
      // + " services matches OpenUDID");

      if (manager.mMatchingIntents != null)
      // Start services one by one
      {
        manager.startService();
      }
    } else {// Got it, you can now call getOpenUDID()
      // if (LOG)
      // com.homelink.util.LogUtil.d(TAG, "OpenUDID: " + OpenUDID);
      mInitialized = true;
    }
  }

  /*
   * Used to sort the OpenUDIDs collected by occurrence
   */
  /*private class ValueComparator implements Comparator<String> {
    public int compare(String a, String b) {
      if (mReceivedOpenUDIDs.get(a) < mReceivedOpenUDIDs.get(b)) {
        return 1;
      } else if (mReceivedOpenUDIDs.get(a) == mReceivedOpenUDIDs.get(b)) {
        return 0;
      } else {
        return -1;
      }    }
  }*/

  private class ValueComparator implements Comparator<String> {
    @Override public int compare(String lhs, String rhs) {

      Integer a = mReceivedOpenUDIDs.get(lhs);
      Integer b = mReceivedOpenUDIDs.get(rhs);

      if (a != null && b != null) {
        return b - a;
      } else {
        return -1;
      }
    }
  }
}
